/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *	  http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.milib.dbg.view {
	import com.googlecode.milib.base.IMLBroadcasterListener;
	import com.googlecode.milib.base.MLBroadcastClass;
	import com.googlecode.milib.data.BrowseHistory;
	import com.googlecode.milib.data.MLCollection;
	import com.googlecode.milib.dbg.MLDBG;
	import com.googlecode.milib.dbg.view.IMLDBGWindowContentProvider;
	import com.googlecode.milib.dbg.view.content.AbstractMLDBGWindowContent;
	import com.googlecode.milib.display.MCDragController;
	import com.googlecode.milib.display.service.MLButton;
	import com.googlecode.milib.display.service.MLMC;
	import com.googlecode.milib.display.ui.scroller.MCScrollerWithUpDnButtons;
	import com.googlecode.milib.service.ISharedObjectsServicedObject;
	import com.googlecode.milib.service.SharedObjectsObjectService;
	import com.googlecode.milib.service.singleton.KeyboardService;
	import com.googlecode.milib.util.MLMCUtil;
	import com.googlecode.milib.vo.MLEventInfo;

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.MovieClip;
	import flash.display.PixelSnapping;
	import flash.display.Sprite;
	import flash.events.TextEvent;
	import flash.filters.BlurFilter;
	import flash.filters.DropShadowFilter;
	import flash.text.AntiAliasType;
	import flash.text.StyleSheet;
	import flash.text.TextField;
	import flash.utils.Dictionary;

	/**
	 * @author Marek Brun
	 */
	public class MLDBGWindow extends MLBroadcastClass implements IMLBroadcasterListener, ISharedObjectsServicedObject {

		public const event_Select : String = 'Select';
		
		public const event_Close : String = 'Close';
		
		//DATA:	AbstractcontentProvider
		public const event_NewContentProvider : String = 'NewContentProvider';
		
		public function get event_SharedObjectDataChanged():String { return 'SharedObjectDataChanged'; }
		
		public var mlmc:MLMC;
		private var mc_tfNames : TextField;
		private var mc_tfTitle : TextField;
		private var bodyBitmap:Bitmap;
		private var mc_btnCorner : MovieClip;		private var width : Number=250;		private var height : Number=400;
		private var btnCorner:MLButton;
		private var dragBtnCorner:MCDragController;
		private var mc_body:MovieClip;
		private var beamBitmap:Bitmap;
		private var mc_up:MovieClip;
		private var btnClose:MLButton;
		private var isSelected:Boolean=true;
		private var isMinimalized:Boolean=false;
		private var contents:MLCollection;
		private var mc_content:MovieClip;
		private var currentContent:AbstractMLDBGWindowContent;
		private var scroller:MCScrollerWithUpDnButtons;
		private var title:String;
		private var btnBeam:MLButton;
		private var uniqueName:String;
		private var dragWindow:MCDragController;
		private var btn:MLButton;
		private static const servicedObjects : Dictionary = new Dictionary(true);		private static const blurWhenDisabled:BlurFilter=new BlurFilter(4, 4, 1);
		private var mldbg:MLDBG;
		private var contentProvider:IMLDBGWindowContentProvider;
		private var isMinimalizedBeforeDeselect:Boolean;
		private var history:BrowseHistory;
		private var btnHistoryFront:MLButton;
		private var btnHistoryBack:MLButton;
		private var isHistoryEnabled:Boolean=true;		private static const dropShadowWhenEnabled:DropShadowFilter=new DropShadowFilter(3, 45, 0, .2);
		private var canBeDisabled:Boolean=true;
		private var mc_contentMask:Sprite;
		private var ks:KeyboardService;
		private var tabSizes:TabSizes;
		private var mc_beamButtons : MovieClip;
		
		public function MLDBGWindow(mc:MovieClip) {
			mlmc=MLMC.forInstance(mc);
			mldbg=MLDBG.getInstance();
			mc_up=mlmc.getMC('up');			mc_body=mlmc.getMC('body');
			mc_tfNames=mlmc.getTextField('body.tfNames');
			mc_tfTitle=mlmc.getTextField('up.tfTitle');
			mc_tfTitle.htmlText='';
			mc_tfTitle.antiAliasType=AntiAliasType.ADVANCED;
			mc_tfTitle.mouseEnabled=false;
			
			mc_tfNames.text='hello world';
			mc_btnCorner=mlmc.getMC('body.btnCorner');
			
			mc_beamButtons=mlmc.getMC('up.beamButtons');
			
			btnBeam=MLButton.createFromMCBoundBox(mlmc.getMC('up.bg'));
			btnBeam.addListener(this);
			btnBeam.btn.alpha=0;
			mlmc.getMC('up').setChildIndex(btnBeam.btn, 0);
			
			btnHistoryFront=mlmc.getMLButton('up.beamButtons.btnHistoryFront');
			btnHistoryFront.setIsEnabled(false);
			btnHistoryFront.addListener(this);			btnHistoryBack=mlmc.getMLButton('up.beamButtons.btnHistoryBack');
			btnHistoryBack.setIsEnabled(false);
			btnHistoryBack.addListener(this);
			btnClose=mlmc.getMLButton('up.beamButtons.btnClose');
			btnClose.addListener(this);
						btnCorner=mlmc.getMLButton('body.btnCorner');
			btnCorner.addListener(this);
			
			if(!SliceProvider.isInitialized()){
				SliceProvider.init(mlmc.getMC('body.border'), mlmc.getMC('up.bg'));
			}
			mc_body.removeChild(mlmc.getMC('body.border'));
			mc_up.removeChild(mlmc.getMC('up.bg'));
			
			bodyBitmap=new Bitmap(new BitmapData(1, 1), PixelSnapping.ALWAYS, true);
			mc_body.addChildAt(bodyBitmap, 0);
			beamBitmap=new Bitmap(new BitmapData(1, 1), PixelSnapping.ALWAYS, true);
			mc_up.addChildAt(beamBitmap, 0);
			
			dragBtnCorner=new MCDragController(mc_btnCorner, btnCorner, false);
			dragBtnCorner.addListener(this);
			
			mc_content=mlmc.getMC('body.content');
			mc_contentMask=MLMCUtil.createRect();
			mc_content.parent.addChild(mc_contentMask);
			mc_contentMask.x=mc_content.x;			mc_contentMask.y=mc_content.y;
			mc_content.mask=mc_contentMask;
			
			tabSizes=TabSizes.getInstance();
			
			scroller=new MCScrollerWithUpDnButtons(mlmc.getMC('body.scroller'), false);
			scroller.addListener(this);
			
			dragWindow=new MCDragController(mc, btnBeam, false);
			dragWindow.addListener(this);
			
			history=new BrowseHistory();
			history.addListener(this);
			
			var style:StyleSheet = new StyleSheet();
			style.parseCSS("a:hover{text-decoration: underline;}");
			mc_tfNames.styleSheet=style;
			mc_tfNames.selectable=true;
			mc_tfNames.addEventListener(TextEvent.LINK, onTFNamesLink, false, 0, true);
			
			draw();
			
			contents=new MLCollection();
			contents.addListener(this);
			
			btn=MLButton.forInstance(mlmc.mc);
			btn.disableButtonMode();
			btn.addListener(this);
			
			ks=KeyboardService.getInstance();
			
			setIsSelected(false);
		}

		public function setTitle(title:String):void {
			this.title=title;
		}
		
		public function addContent(content:AbstractMLDBGWindowContent):void {
			contents.add(content);
		}
		
		public function setContent(content:AbstractMLDBGWindowContent):void {
			if(currentContent==content){ return; }
			unsetCurrentContent();
			currentContent=content;
			mc_content.addChild(currentContent.mlmc.mc);
			scroller.mc.visible=getIsScrollerVisible();
			if(tabSizes.gotSizeForTabName(currentContent.getTabName())){
				setSize(tabSizes.getSizeForTabName(currentContent.getTabName())[0], tabSizes.getSizeForTabName(currentContent.getTabName())[1]);
			}else{
				updateContentSize();
			}
			currentContent.setIsEnabled(true);
			currentContent.addListener(this);
			drawContentsTabs();
			drawTitle();
		}
		
		protected function unsetCurrentContent():void {
			if(currentContent){
				mc_content.removeChild(currentContent.mlmc.mc);
				currentContent.setIsEnabled(false);
				currentContent.removeListener(this);
				currentContent=null;
			}
		}
		
		public function disableDisable():void {
			setIsSelected(true);
			canBeDisabled=false;
		}
		
		public function removeContent(content:AbstractMLDBGWindowContent):void {
			contents.remove(content);
		}
		
		public function removeContents():void {
			var i:uint;
			var loopContent:AbstractMLDBGWindowContent, contentsArr:Array=contents.toArray();
			for(i=0;i<contentsArr.length;i++){
				loopContent=contentsArr[i];
				removeContent(loopContent);
			}
		}
		
		public function addContents(contents:Array):void {
			var i:uint;
			var loopContent:AbstractMLDBGWindowContent;
			for(i=0;i<contents.length;i++){
				loopContent=contents[i];
				addContent(loopContent);
			}
		}
		
		protected function setSize(width:Number, height:Number):void {
			this.width=Math.max(width, SliceProvider.getInstance().getMinWidth());
			this.height=Math.max(height, SliceProvider.getInstance().getMinHeight());
			if(currentContent) {
				tabSizes.setSizeForTabName(currentContent.getTabName(), width, height);
			}
			updateContentSize();
			draw();
		}
		
		public function setPosition(x:Number, y:Number):void {
			mlmc.mc.x=x;			mlmc.mc.y=y;
		}
		
		protected function updateContentSize():void {
			if(!currentContent){ return; }
			if(scroller.mc.visible){
				mc_contentMask.width=width-11-scroller.mc.width;				mc_contentMask.height=height-40;
				currentContent.setSize(width-11-scroller.mc.width, height-40);
			}else{
				mc_contentMask.width=width-11;
				mc_contentMask.height=height-40;
				currentContent.setSize(width-11, height-40);
			}
		}
		
		protected function draw():void {
			bodyBitmap.bitmapData.dispose();
			bodyBitmap.bitmapData=SliceProvider.getInstance().getBGBitmapData(width, height);
			btnCorner.btn.x=width;			btnCorner.btn.y=height;
			
			beamBitmap.bitmapData.dispose();
			beamBitmap.bitmapData=SliceProvider.getInstance().getBeamBitmapData(width);
			mc_beamButtons.x=width;
			btnBeam.btn.width=width;
			mc_tfNames.width=width-mc_tfNames.x;			mc_tfTitle.width=width-mc_tfTitle.x-mc_beamButtons.width-2;
			
			scroller.mc.x=width-scroller.mc.width-5;
			scroller.setLength(height-42);
			
		}
		
		protected function drawContentsTabs():void {
			var names:Array=[];
			var contentsArr:Array=contents.toArray();
			var i:uint;
			var loopContent:AbstractMLDBGWindowContent;
			for(i=0;i<contentsArr.length;i++){
				loopContent=contentsArr[i];
				if(loopContent==currentContent){
					names.push('<b>'+loopContent.getTabName()+'</b>');
				}else{
					names.push('<A HREF="event:'+i+'" >'+loopContent.getTabName()+'</A>');
				}
			}
			mc_tfNames.htmlText=names.join(' | ');
			
		}
		
		protected function drawTitle():void {
			if(title){
				mc_tfTitle.htmlText='<b>'+title+'</b>';
				if(currentContent.getTitle()){
					mc_tfTitle.htmlText+=' :: '+currentContent.getTitle();
				}
			}else if(currentContent.getTitle()){
				mc_tfTitle.htmlText=currentContent.getTitle();
			}
		}
		
		public function setIsSelected(selected:Boolean):void {
			if(isSelected==selected){ return; }			isSelected=selected;
			if(isSelected){
				ks.addListener(this);
				mlmc.mc.filters=[dropShadowWhenEnabled];
				if(canBeDisabled){
					btn.btn.mouseEnabled=false;
					mlmc.mc.cacheAsBitmap=true;
					mlmc.mc.mouseChildren=true;
					mlmc.mc.alpha=1;
					setIsMinimalized(isMinimalizedBeforeDeselect);
				}
				broadcastEvent(event_Select);
			}else{
				if(canBeDisabled){
					ks.removeListener(this);
					btn.btn.mouseEnabled=true;
					mlmc.mc.cacheAsBitmap=false;
					mlmc.mc.mouseChildren=false;
					mlmc.mc.filters=[blurWhenDisabled];
					mlmc.mc.alpha=.2;
					isMinimalizedBeforeDeselect=isMinimalized;
					setIsMinimalized(true);
				}
			}
		}
		
		public function setIsMinimalized(minimalized:Boolean):void {
			if(isMinimalized==minimalized){ return; }
			isMinimalized=minimalized;
			if(isMinimalized){
				mlmc.mc.removeChild(mc_body);
			}else{
				mlmc.mc.addChild(mc_body);
			}
		}
		
		public function close():void {
			if(btnClose.btn.parent){
				broadcastEvent(event_Close);
			}
		}
		
		public function hideCloseButton():void {
			btnClose.btn.parent.removeChild(btnClose.btn);
		}
		
		public function disableHistory():void {
			isHistoryEnabled=false;
			btnHistoryBack.btn.parent.removeChild(btnHistoryBack.btn);			btnHistoryFront.btn.parent.removeChild(btnHistoryFront.btn);
		}

		public function getSharedObjectData():Object {
			return {
				x:mlmc.mc.x,				y:mlmc.mc.y,				width:width,				height:height
			};
		}
		
		public function getSharedObjectInitData():Object {
			return {
				x:10,
				y:10,
				width:400,
				height:300
			};
		}
		
		public function applyCookieData(data:Object):void {
			setPosition(data.x, data.y);
			setSize(data.width, data.height);
		}
		
		public function getSharedObjectUniqueName():String {
			return uniqueName;
		}
		
		public function setUniqueName(uniqueName:String):void {
			this.uniqueName=uniqueName;
			SharedObjectsObjectService.forInstance(this);
		}
		
		protected function getIsScrollerVisible():Boolean {
			return scroller.getVisibleToTotal()<1 && currentContent.getIsScrollable();
		}

		protected function resizeIfScrollerVisibleChange():void {
			if(scroller.mc.visible!=getIsScrollerVisible()){
				scroller.mc.visible=getIsScrollerVisible();
				updateContentSize();
			}
		}
		
		public function setContentProvider(contentProvider:IMLDBGWindowContentProvider, isHistoryAdd:Boolean=true):void {
			if(this.contentProvider==contentProvider){ return; }
			if(isHistoryEnabled && isHistoryAdd){ history.add(contentProvider); }
			this.contentProvider=contentProvider;
			setTitle(contentProvider.getMLDBGWindowTitle());
			removeContents();
			addContents(contentProvider.getMLDBGWindowContents());
			broadcastEvent(event_NewContentProvider, contentProvider);
		}
		
		public static function forInstance(obj:Object):MLDBGWindow {
			if(servicedObjects[obj]){
				return servicedObjects[obj];
			}else{
				servicedObjects[obj]=new MLDBGWindow(MLDBGStage.getInstance().getNewMLDBGMC());
				MLDBGStage.getInstance().addMLDBGWindow(servicedObjects[obj]);
				MLDBGWindow(servicedObjects[obj]).mlmc.mc.x=Math.max(10, MLDBGWindow(servicedObjects[obj]).mlmc.mc.parent.mouseX-10);
				MLDBGWindow(servicedObjects[obj]).mlmc.mc.y=Math.max(10, MLDBGWindow(servicedObjects[obj]).mlmc.mc.parent.mouseY-10);
				return servicedObjects[obj];
			}
		}
		
		public function setContentByIndex(index:uint):void {
			setContent(contents.toArray()[index]);
		}
		
//********************************************************************************************
//		events for MLDBGWindow
//********************************************************************************************
		public function onEvent(ev:MLEventInfo):void {
			switch(ev.hero){
				case dragBtnCorner:
					switch(ev.event) {
						case dragBtnCorner.event_Start:
							mlmc.mc.cacheAsBitmap=false;
						break;
						case dragBtnCorner.event_NewDragXY:
							setSize(dragBtnCorner.getX(), dragBtnCorner.getY());
						break;
						case dragBtnCorner.event_Stop:
							mlmc.mc.cacheAsBitmap=true;
							broadcastEvent(event_SharedObjectDataChanged);
						break;
					}
				break;
				case dragWindow:
					switch(ev.event) {
						case dragWindow.event_NewDragXY:
							setPosition(dragWindow.getX(), dragWindow.getY());
						break;
						case dragWindow.event_Stop:
							broadcastEvent(event_SharedObjectDataChanged);
						break;
					}
				break;
				case btnClose:
					switch(ev.event) {
						case btnClose.event_Press:
							close();
						break;
					}
				break;
				case contents:
					switch(ev.event) {
						case contents.event_ValueAdded:
							ev.data.setupScroller(scroller);
							if(contents.getLength()==1){
								setContent(ev.data);
							}else{
								drawContentsTabs();
							}
						break;
						case contents.event_ValueRemoved:
							if(ev.data==currentContent){
								unsetCurrentContent();
							}
							drawContentsTabs();
						break;
					}
				break;
				case currentContent:
					switch(ev.event) {
						case currentContent.event_TitleChanged:
							drawTitle();
						break;
						case currentContent.event_Link:
							MLDBG.getInstance().exeLink(TextEvent(ev.data).text, isHistoryEnabled ? this : null);
						break;
					}
				break;
				case scroller:
					switch(ev.event) {
						case scroller.event_VisibletoTotalChanged:
							resizeIfScrollerVisibleChange();
						break;
					}
				break;
				case btnBeam:
					switch(ev.event) {
						case btnBeam.event_DoublePress:
							setIsMinimalized(!isMinimalized);
						break;
					}
				break;
				case btn:
					switch(ev.event) {
						case btn.event_Press:
							setIsSelected(true);
						break;
					}
				break;
				case btnHistoryFront:
					switch(ev.event) {
						case btnHistoryFront.event_Press:
							history.forward();
						break;
					}
				break;
				case btnHistoryBack:
					switch(ev.event) {
						case btnHistoryBack.event_Press:
							history.back();
						break;
					}
				break;
				case history:
					switch(ev.event) {
						case history.event_Changed:
							setContentProvider(history.getCurrentHistoryStepID(), false);
							btnHistoryBack.setIsEnabled(history.getCanGoBack());							btnHistoryFront.setIsEnabled(history.getCanGoForward());
						break;
					}
				break;
				case ks:
					switch(ev.event) {
						case ks.event_Down:
							switch(ev.data.keyCode) {
								case KeyboardService.KEY_F5:
									if(!currentContent){ break; }
									currentContent.refresh();
								break;
								case KeyboardService.KEY_BACK:
									history.back();
								break;
								case KeyboardService.KEY_ESC:
									close();
								break;
							}
						break;
					}
				break;
			}
		}
		
		protected function onTFNamesLink(event:TextEvent):void {
			setContent(contents.toArray()[uint(event.text)]);
		}
	}
}

import com.googlecode.milib.base.MLBroadcastClass;
import com.googlecode.milib.display.Scale9BitmapData;
import com.googlecode.milib.service.ISharedObjectsServicedObject;
import com.googlecode.milib.service.SharedObjectsObjectService;

import flash.display.BitmapData;
import flash.display.MovieClip;

internal class SliceProvider{
	
	private static var instance:SliceProvider;
	private var bodySliceProvider:Scale9BitmapData;
	private var beamSliceProvider:Scale9BitmapData;

	public function SliceProvider(mc_body:MovieClip, mc_beam:MovieClip) {
		bodySliceProvider=Scale9BitmapData.createFromSlicedMC(mc_body);		beamSliceProvider=Scale9BitmapData.createFromSlicedMC(mc_beam);
		
	}
	
	public function getBGBitmapData(width:Number, height:Number):BitmapData {
		return bodySliceProvider.getScaled9BitmapData(width, height);
	}
	
	public function getBeamBitmapData(width:Number):BitmapData {
		return beamSliceProvider.getScaled9BitmapData(width, beamSliceProvider.getBaseHeight());
	}
	
	public function getMinWidth():Number {
		return bodySliceProvider.getMinWidthWithoutSmashing();
	}
	
	public function getMinHeight():Number {
		return bodySliceProvider.getMinHeightWithoutSmashing()+beamSliceProvider.getBaseHeight();
	}
	
	public static function getInstance():SliceProvider {
		if(instance) { return instance; }
		else{ throw new Error('Before call SliceProvider.getInstance() you should call SliceProvider.init()'); }
	}
	
	public static function init(mc_body:MovieClip, mc_beam:MovieClip):void {
		if(instance) { return; }
		else{ instance=new SliceProvider(mc_body, mc_beam);  }
	}
	
	public static function isInitialized():Boolean {
		return Boolean(instance);
	}
}

internal class TabSizes extends MLBroadcastClass implements ISharedObjectsServicedObject {
	
	public function get event_SharedObjectDataChanged():String { return 'SharedObjectDataChanged'; }
	
	private var tabSizes:Object={};
	private static var instance:TabSizes;
	
	public function TabSizes() {
		SharedObjectsObjectService.forInstance(this);
	}
	
	public function gotSizeForTabName(tabName:String):Boolean {
		return tabSizes.hasOwnProperty(tabName);
	}
	
	public function getSizeForTabName(tabName:String):Array {
		if(tabSizes.hasOwnProperty(tabName)){
			return tabSizes[tabName];
		}
		return null;
	}
	
	public function setSizeForTabName(tabName:String, width:Number, height:Number):void {
		tabSizes[tabName]=[width, height];
		//broadcastEvent(event_SharedObjectDataChanged);
	}
	
	public function getSharedObjectData():Object {
		return {tabSizes:tabSizes};
	}
	
	public function getSharedObjectInitData():Object {
		return {tabSizes:{}};
	}
	
	public function getSharedObjectUniqueName():String {
		return 'MLDBGWindowTabSizes';
	}
	
	public function applyCookieData(data:Object):void {
		tabSizes=data.tabSizes;
	}
	
	public static function getInstance():TabSizes {
		if(instance) { return instance; }
		else{ instance=new TabSizes(); return instance; }
	}
	
}